home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
PROGRAMM
/
ASSEMBLE
/
0938.ZIP
/
KEGELUNX.ARC
/
SPRINTF.ASM
< prev
next >
Wrap
Assembly Source File
|
1985-09-16
|
11KB
|
518 lines
;---- sprintf.asm -------------------------------------------
; An implementation of sprintf in 8086 assembly.
; Calling convention:
; call near sprintf(s, format {,item {,item ...}})
; word char *s, *format;
; Prints the items to the specified string.
; Arguments are pushed from right to left before the call; upon
; return, it is the caller's duty to clean the parameters off the stack.
; All registers used, except BP.
; All pointers are DS-relative except s, which is ES-relative.
; Format string items:
; \n -> cr lf, \r -> cr
; \t -> tab, \f -> form feed, \O -> null, \\ -> \
; \number -> anychar (Number is octal if leading char is zero)
; %% -> %
; %c -> word item's low byte is a char to print
; %d -> word item is to be printed as decimal number
; %x -> word item is to be printed as hex number (no leading 0x, though)
; %s -> word item is a pointer to a null-terminated string
; %nnc, where nn is a field width, and c is d,s, or x ->
; nn<0: left justified; nn>0: right justified
; %hd, %hx -> short word item (default)
; %ld, %lx -> long word item; high order word pushed first.
; Unrecognized % or \ sequences act as if the % or \ was not given.
;---------------------------------------------------------------
PUBLIC sprintf
code segment para public 'CODE'
assume cs:code
; main routine variables
lpc db ? ; kludge - left padding character
; i_to_a variables
i2a_fw dw ?
i2a_buf dw ?
i2a_lpc db ? ; left padding char- either ' ' or '0'
sprintf proc near
push bp
mov bp,sp
mov si,4 ; offset of first argument from bp
cld ; strings increment
; di = s;
mov di, [bp+si] ; di gets target string pointer (ES relative)
add si, 2
; bx = format;
mov bx, [bp+si] ; bx gets format string pointer (DS relative)
add si, 2 ; bump si to first vararg
; while (al = *bx++) {
spf_while:
mov al, byte ptr [bx]
inc bx
or al, al
jnz spfbody
jmp spfdone
spfbody:
; if (al == '\') {
cmp al, '\'
jnz not_backsl
;-------- BEGIN BACKSLASH --------
; al = *bx++;
mov al, byte ptr [bx]
inc bx
or al, al
jnz bs_nnuke
jmp spfdone
bs_nnuke:
; if (al=='\')
cmp al, '\'
jz storem
; else if (al=='f')
cmp al, 'f'
jnz bs_nf
mov al, 12
jmp short storem
bs_nf: ; else if (al=='n')
cmp al, 'n'
jnz bs_nn
mov al, 13
stosb
mov al, 10
jmp short storem
bs_nn: ; else if (al=='O')
cmp al, 'O'
jnz bs_nO
mov al, 0
jmp short storem
bs_nO: ; else if (al=='r')
cmp al, 'r'
jnz bs_nr
mov al, 13
jmp short storem
bs_nr: ; else if (al=='t')
cmp al, 't'
jnz bs_nt
mov al, 9
jmp short storem
bs_nt: ; else if (isdigit(al)) {
cmp al, '0'
jb storem
cmp al, '9'
ja storem
dec bx ; point to numeric str
call a_to_i ; result to ax
storem: stosb
jmp spfwend
;----------- END BACKSLASH -----------
not_backsl: ; } else if (al != '%')
cmp al, '%'
jnz storem ; ---- NORMAL CHARS ----
; else { /* al == '%' */
;----------- BEGIN PERCENT ----------
; al = *bx++;
mov al, byte ptr [bx]
inc bx
or al, al
jz spfdgate
; if (al == '%')
cmp al, '%'
jz storem
; fieldwidth = DEFAULT;
mov cx, 08000h
; if (al == '-' || isdigit(al)) {
cmp al, '-'
jz pc_npc
cmp al, '9'
ja pc_nfw
cmp al, '0'
jb pc_nfw
; if (al == '0') leftpadchar = '0' else ' '
mov lpc, ' '
jnz pc_npc
mov lpc, '0'
inc bx ; skip zero
pc_npc:
; fieldwidth = a_to_i(&bx);
dec bx
call a_to_i
mov cx, ax
; if (!(al=*bx++)) break;
mov al, [bx]
inc bx
or al, al
jnz pc_nfw
spfdgate: jmp spfdone
pc_nfw:
; At this point, CX contains the fieldwidth, or
; 8000h for default, and AL contains next character.
; size = short
mov ah, 0
; if (al == 'l')
cmp al, 'l'
jnz pc_nh
; size = long
mov ah, 1
mov al, [bx]
inc bx
or al, al
jz spfdgate
jmp short pc_nh ; don't allow l AND h
pc_nl:
; if (al == 'h')
cmp al, 'h'
jnz pc_nh
; default is short; just throw away 'h'
mov al, [bx]
inc bx
or al, al
jz spfdgate
pc_nh:
; radix = 10
mov dx, 10
; if (al == 'd')
cmp al, 'd'
jz got_integer
cmp al, 'x'
jnz pc_ni
; radix = 16
mov dx, 16
got_integer:
push bx ; save format ptr
mov bx, dx ; set radix
; if (size == long)
cmp ah, 1
mov ax, [bp+si] ; get ax=item
mov dx, 0 ; high word zero
jnz pc_short
add si,2
mov dx,[bp+si]
pc_short:
add si, 2
mov bh, lpc ; set left pad char
call i_to_a
pop bx
jmp spfwend
pc_ni: ; if (al == 'c')
cmp al, 'c'
jnz pc_nc
; Jest a char.
mov ax, [bp+si]
add si, 2
jmp storem
pc_nc: ; if (al == 's')
cmp al, 's'
jnz pc_ns
mov ax, [bp+si]
add si, 2
push si
mov si, ax
call doprint
pop si
jmp spfwend
pc_ns: ; bad format char
stosb
spfwend: jmp spf_while
spfdone:
mov al, 0
stosb
pop bp
ret
sprintf endp
;----- doprint -----------------------------------------------
; Input string pointer in DS:SI, output string pointer in ES:SI,
; field width in CX.
; Preserves BX, uses all others.
doprint proc near
push bx
mov bx, cx
; find length
push si
mov cx, 0
mov al, 0
lflp: lodsb
or al, al
jz lfex
inc cx
jmp lflp
lfex: pop si
; Pad or truncate?
cmp bx, 8000h
jz dop_xfer ; neither.
cmp bx, cx
jz dop_xfer
cmp bx, 0
jl dop_right
; Field width > 0; pad or truncate on left.
sub bx, cx ; bx = field width - string length
jl dop_tl ; truncate on left
; pad on left
push cx
mov cx, bx
mov al, ' '
rep stosb
pop cx
jmp short dop_xfer
dop_tl: ; Truncate; bx is negative dist to truncate.
; only if dot specified
; sub si, bx ; add truncate size to start
; add cx, bx ; and sub from length
jmp short dop_xfer
dop_right:
; Field width < 0; pad or truncate on right.
neg bx
sub bx, cx
jl dop_tr ; truncate on right
; pad on right.
rep movsb ; do the copy
mov cx, bx
mov al, ' '
rep stosb
jmp short dop_done
dop_tr: ; truncate on right; bx is negative
; only if dot specified
; add cx, bx
dop_xfer:
rep movsb
dop_done:
pop bx
ret
doprint endp
;---- i_to_a -------------------------------------------------------------
; Input integer in DX:AX; output radix in BL; output buffer pointer in ES:DI,
; Field width in CX, left padding char in BH.
; Preserves SI, BP.
; Does not terminate string with a null; lets caller do it by
; mov al, 0
; stosb
; if he likes.
i_to_a proc near
push si
push bp
mov i2a_fw, cx
mov i2a_buf, di
mov i2a_lpc, bh ; save padding char
mov bh, 0 ; make radix a word
call dw_stackem ; convert DX:AX to BCD radix BX on stack
; returns number of digits in CX.
mov bx, i2a_fw
mov di, i2a_buf
; Check out the padding situation
; Pad on left if field width > 0
cmp bx, 8000h
jz ttprint
cmp bx, 0
jle ttprint
; If digit string length < field width, pad it.
mov bp, bx
sub bp, cx
jle ttprint
mov al, i2a_lpc
xchg cx, bp
rep stosb
xchg cx, bp
ttprint:
mov bp, cx ; save string length
; loop2: print them
ttd2: pop ax
cmp al, 10
jb tt_decimal
add al, 7
tt_decimal:
add al, '0'
stosb
loop ttd2
; Check out the padding situation on other side
cmp bx, 8000h ; if (field width == DEFAULT)
jz ttdone ; don't pad
; Pad on right if field width >0
neg bx
jle ttdone
; If digit string length < field width, pad it.
sub bx, bp
jle ttdone
mov al, ' '
mov cx, bx
rep stosb
ttdone:
pop bp
pop si ; restore vararg
ret
i_to_a endp
;----- a_to_i -------------------------------------------------
; Given bx pointing to the leading digit of a numeric string, or a
; minus sign, returns the value of that field in AX.
; Returns BX pointing to first non-numeric char.
; Preserves SI and DI, changes all others.
a_to_i proc near
push si
push [bx]
cmp byte ptr [bx], '-'
jnz at_notneg
inc bx
at_notneg:
; radix = leading_zero ? 8 : 10;
mov si, 10
cmp al, '0'
jnz at_dec
mov si, 8
at_dec: ; fieldwidth = 0
mov cx, 0
at_lp: ; while ((al=*bx) && isdigit(al))
mov al, [bx]
or al, al
jz at_ex
cmp al, '0'
jb at_ex
cmp al, '9'
ja at_ex
; fw = fw*radix + al-'0';
sub al, '0'
mov ah, 0
xchg ax, cx
mul si
add cx, ax
; bx++;
inc bx
jmp at_lp
at_ex:
xchg ax, cx ; get value to ax
pop cx ; get first char agin
cmp cl, '-'
jnz at_dontneg
neg ax
at_dontneg:
pop si
ret
a_to_i endp
;------ dw_stackem ------------------------------------------------
; Converts a longword to an unpacked BCD string on the stack.
; Input: longword in DX:AX, output radix in BX (must be 10 or 16)
; Output: cx=number of digits, TOS=leftmost digit...LOS=rightmost
; Uses all registers except DI, including BP.
; Returns radix unchanged in BX.
dw_stackem proc near
pop bp ; return address to BP
xor cx, cx
cmp bx, 10
jz dws_dec
; Begin stacking hexadecimal digits.
dws_hl: mov si, 0 ; clear remainder
shr dx,1 ; divide by 2
rcr ax,1
rcr si,1
shr dx,1 ; divide by 2
rcr ax,1
rcr si,1
shr dx,1 ; divide by 2
rcr ax,1
rcr si,1
shr dx,1 ; divide by 2
rcr ax,1
rcr si,1
push cx
mov cl,4
rol si,cl
pop cx ; ech
push si ; push remainder of division
inc cx
or ax,ax
jnz dws_hl
jmp bp
dws_dec:
; Divide the number by 100000; stack remainder, then quotient.
; The trick is to divide by 2, then by 50000.
; The overall remainder is (x & 1) + (x/2 % 50000)*2.
shr dx, 1 ; high word
rcr ax, 1 ; low word; remainder in C.
pushf
mov bx, 50000
div bx ; unsigned divide; remainder to dx.
mov si, ax ; quotient to si for later use.
mov ax, dx ; ax is now low word of remainder
mov dx, 0
add ax, ax ; dx:ax = (x/2 % 50000) * 2
adc dx, 0 ; carry to high word
popf
adc ax, 0 ; dx:ax += (x & 1)
adc dx, 0 ; carry to high word
mov bx, 10
;------ SI = N/100000; BX = radix; DX:AX = N % 100000 ---------------
; Stack digits, starting with the low digit of the remainder.
; Blank leading zeroes.
; Count digits produced in CX.
; loop1: stack BCD digits of remainder
; If quotient zero, suppress leading zeroes.
mov cx, 0
dtd1: div bx
push dx ; push remainder of division
xor dx, dx ; clear remainder
inc cx
or ax, ax ; If any digits left, not done.
jnz dtd1
or si, si ; else if Quotient from above zero, done.
jz dtd1_x
cmp cx, 5 ; else if < 5 digits printed, not done.
jnz dtd1
dtd1_x:
; loop2: stack BCD digits of quotient
mov ax, si
or ax, ax
jz d2ddone
dtd2: xor dx,dx
div bx
push dx ; push remainder of division
inc cx
or ax,ax
jnz dtd2
d2ddone:
jmp bp ; return
dw_stackem endp
code ends
end